import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Parser {
//先指定一个加载文档的路径
    private static final String INPUT_PATH = "D:/Download/docs/api";
//创建一个Index实例
    private Index index = new Index();
    public void run(){
        long beg = System.currentTimeMillis();
        System.out.println("索引制作开始");
        //整个类的入口
        //1.根据上面指定的路径,枚举出该路径中所有的文件(html).这个过程需要把所有子目录中的文件都能获取到
       ArrayList<File>fileList = new ArrayList<>();
         enumFile(INPUT_PATH,fileList);
 //  * System.out.println(fileList);
// * System.out.println(fileList.size());
        //2.针对上面罗列出的文件路径,打开文件,读取文件内容,并进行解析,并构建索引.
       for(File f:fileList){//时间主要花在这
            //通过这个方法来解析单个的HTML文件
            System.out.println("开始解析:"+f.getAbsolutePath());

            parseHTML(f);
        }
        //3.把在内存中构造的索引数据结构,保存到指定的文件中.
      index.save();
        long end = System.currentTimeMillis();
        System.out.println("索引制作结束!消耗时间:"+(end-beg)+"ms");
    }


//通过这个方法实现"多线程制作索引"
    public void runByThread() throws InterruptedException {
        long beg = System.currentTimeMillis();
        System.out.println("索引制作开始!");


        //1.枚举出所有的文件
        ArrayList<File> files = new ArrayList<>();
        enumFile(INPUT_PATH,files);
        //2.循环遍历文件,此处为了能通过多线程制作索引,就直接引入线程池.
        CountDownLatch latch = new CountDownLatch(files.size());//为了保证所有线程运行完,才存起来,用到这个函数,撞线
        ExecutorService executorService = Executors.newFixedThreadPool(4);//4个线程
        for (File f:files) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("解析"+f.getAbsolutePath());
                    parseHTML(f);
                    latch.countDown();
                }
            });
            //await方法会阻塞,直到所有的选手都调用 countDown撞线之后,才能阻塞结束
            latch.await();
//3.保存索引

index.save();
            long end = System.currentTimeMillis();
            System.out.println("索引制作完毕!消耗时间:"+(end-beg)+"ms");

        }


    }
    private void parseHTML(File f) {
        //1.解析出HTML的标题
        String title = parseTitle(f);
        //2.解析出HTML对应的URL
        String url = parseUrl(f);
        //3.解析出HTML对应的正文(有正文才能有后序的描述)
        String content = parseContent(f);
//4.把解析出来的这些信息加入到索引中
        index.addDoc(title,url,content);//(Index3.中的顺序保持一致
    }

    public String parseContent(File f) {
        //先按照一个字符一个字符的方式读取,以<和>来控制拷贝数据的开关.(字符要用FileReader)
        try {
            FileReader fileReader = new FileReader(f);//(报错,文件不存在
           //加上一个是否要进行拷贝的开关
            boolean isCopy = true;
            //还得准备一个保存结果的StringBuilder(线程不安全)
            StringBuilder content = new StringBuilder();
            while(true) {
                //注意read此处返回值是int,不是char
                //按理说read,应该是一次读一个字符,就返回char就够了呀?
                //此处使用int作为返回值,主要是为了表示一些非法情况
                //如果读到了文件末尾,继续读,就会返回-1
                int ret = fileReader.read();//(报错)输入输出异常,
                //这里的read是一次读一个字符
                if (ret == -1) {
                    //表示文件读完了
                    break;
                }
                //如果这个结果不是-1,那么就是一个合法的字符了.
                char c = (char) ret;
                if (isCopy) {
                    //开关打开的状态,遇到普通字符就应该拷贝到StringBuilder中
                    if (c == '<') {
                        //关闭开关
                        isCopy = false;
                        continue;
                    }
                    if(c=='\n'||c=='\r'){  //这两个都是换行的意思,下面这个部分做出修改.因为运行一大段的空白
                        //为了去掉换行,把换行替换成空格了
                        c=' ';
                    }
                    //其他字符,直接进行拷贝即可,把结果给拷贝到最终的StringBuilder中
                    content.append(c);
                } else {
                    //开关关闭的状态,就暂时不拷贝,直到遇到>
                    if(c == '>'){
                       isCopy=true;

                    }
                }
            }
            fileReader.close();
            return content.toString();
        }catch(IOException e) {
            e.printStackTrace();
        }

        return "";//(content在里面的大括号)(如果抛异常就返回空字符串)
    }

    private String parseUrl(File f) {
        String part1 = "https://docs.oracle.com/en/java/javase/19/docs/api";
        String part2 = f.getAbsolutePath().substring(INPUT_PATH.length());
        return part1 + part2;
    }
    private String parseTitle(File f) {
        String name = f.getName();
        return name.substring(0,name.length()-".html".length());

    }



    //第一个参数表示从哪个目录开始进行递归遍历.
         //第二个参数表示递归得到的结果.
    private void enumFile(String inputPath, ArrayList<File> fileList) {
        File rootPath = new File(inputPath);
        String[] fileLists = rootPath.list();

        File[] files = rootPath.listFiles();
        for (File f : files) {

            //根据当前f的类型,来决定是否要递归.
            //如果f是一个普通文件,把f加入到fileList结果中
            //如果f是一个目录,就递归调用enumFile这个方法,来进一步获取子目录中的内容.
            if (f.isDirectory()) {//是不是目录
                enumFile(f.getAbsolutePath(), fileList);
            } else {
                if (f.getAbsolutePath().endsWith(".html")) {//Strin g里的方法,初
                fileList.add(f);
              }
        }
    }

}
    public static void main(String[] args) throws InterruptedException {
        //用main方法实现整个制作索引的过程
        Parser parser = new Parser();
        parser. runByThread();

    }
}
